package com.wenchao.libbluetooth;

import android.annotation.SuppressLint;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothHidDevice;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;
import android.util.Log;
import android.view.InputDevice;
import android.widget.Toast;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

/**
 * Bluetooth 管理类
 *
 * @author ifeegoo www.ifeegoo.com
 */
public class SuperBluetoothManager {

    private final String TAG = SuperBluetoothManager.class.getSimpleName();

    private static volatile SuperBluetoothManager mManager;

    private BluetoothAdapter bluetoothAdapter;

    static BluetoothScanResultCallback bluetoothScanResultCallback;

    static BluetoothPairCallback bluetoothPairCallback;

    static BluetoothConnectCallback bluetoothConnectCallback;

    static BluetoothDataSendReceiveCallback dataSendReceiveCallback;

    private static final int CONNECT_SUCCESS = 0x01;
    private static final int CONNECT_FAILURE = 0x02;
    private static final int DISCONNECT_SUCCESS = 0x03;

    private static final int SEND_SUCCESS = 0x04;
    private static final int SEND_FAILURE = 0x05;

    private static final int RECEIVE_SUCCESS = 0x06;
    private static final int RECEIVE_FAILURE = 0x07;

    private static final int START_DISCOVERY = 0x08;
    private static final int STOP_DISCOVERY = 0x09;
    private static final int DISCOVERY_DEVICE = 0x0A;

    private static final int DEVICE_BOND_NONE = 0x0B;
    private static final int DEVICE_BONDING = 0x0C;
    private static final int DEVICE_BONDED = 0x0D;

    /**
     * 连接设备的UUID
     */
    private static final String MY_BLUETOOTH_UUID = "00001101-0000-1000-8000-00805F9B34FB";  //蓝牙通讯
    /**
     * 发起连接的线程
     */
    private ConnectThread connectThread;
    /**
     * 管理连接的线程
     */
    private ConnectedThread connectedThread;

    private BtBroadcastReceiver btBroadcastReceiver;
    private Context mContext;

    /**
     * 连接超时
     */
    private final Runnable connectTimeoutRunnable = new Runnable() {
        @Override
        public void run() {
            Log.e(TAG, "startConnectDevice-->连接超时");

            Message message = new Message();
            message.what = CONNECT_FAILURE;
            mHandler.sendMessage(message);

            // 断开管理连接
            clearConnectedThread();
        }
    };
    private BluetoothGatt mBluetoothGatt;

    private SuperBluetoothManager() {
    }

    public static SuperBluetoothManager getInstance() {
        if (mManager == null) {
            synchronized (SuperBluetoothManager.class) {
                if (mManager == null) {
                    mManager = new SuperBluetoothManager();
                }
            }
        }
        return mManager;
    }

    /**
     * 初始化蓝牙
     */
    public void initBluetooth(Context context) {
        this.mContext = context;
        bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        if (bluetoothAdapter == null) {
            Toast.makeText(context, "当前手机设备不支持蓝牙", Toast.LENGTH_SHORT).show();
        } else {
            // 手机设备支持蓝牙，判断蓝牙是否已开启
            if (bluetoothAdapter.isEnabled()) {
                Toast.makeText(context, "手机蓝牙已开启", Toast.LENGTH_SHORT).show();
                initBtBroadcast(context);
                initProfileProxy(context);
            } else {
                // 蓝牙没有打开，去打开蓝牙。推荐使用第二种打开蓝牙方式
                // 第一种方式：直接打开手机蓝牙，没有任何提示
//                bluetoothAdapter.enable();  //BLUETOOTH_ADMIN权限
                // 第二种方式：友好提示用户打开蓝牙
                Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
                context.startActivity(enableBtIntent);
            }
        }
    }

    /**
     * 初始化蓝牙广播
     */
    private void initBtBroadcast(Context context) {
        // 注册广播接收
        btBroadcastReceiver = new BtBroadcastReceiver();
        IntentFilter intentFilter = new IntentFilter();
        // 开始扫描
        intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
        // 扫描结束
        intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
        // 搜索到设备
        intentFilter.addAction(BluetoothDevice.ACTION_FOUND);
        // 配对状态监听
        intentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
        intentFilter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
        intentFilter.addAction(BluetoothHidDevice.ACTION_CONNECTION_STATE_CHANGED);
        context.registerReceiver(btBroadcastReceiver, intentFilter);
    }

    /**
     * 取消注册蓝牙广播
     *
     * @param context
     */
    public void unRegisterReceiver(Context context) {
        if (btBroadcastReceiver != null) {
            context.unregisterReceiver(btBroadcastReceiver);
        }
    }

    private void initProfileProxy(Context context) {
        bluetoothAdapter.getProfileProxy(context, mProfileListener, BluetoothProfile.A2DP | BluetoothProfile.HID_DEVICE);
    }

    private BluetoothA2dp mBluetoothA2dpProfile;
    private BluetoothHidDevice mBluetoothHidDevice;
    private BluetoothProfile.ServiceListener mProfileListener = new BluetoothProfile.ServiceListener() {
        @Override
        public void onServiceConnected(int profile, BluetoothProfile proxy) {
            Log.d(TAG, "onServiceConnected, profile = " + profile);
            if (profile == BluetoothProfile.A2DP) {
                mBluetoothA2dpProfile = (BluetoothA2dp) proxy;
            } else if (profile == BluetoothProfile.HID_DEVICE) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
                    mBluetoothHidDevice = (BluetoothHidDevice) proxy;
                }
            }
        }

        @Override
        public void onServiceDisconnected(int profile) {
            Log.d(TAG, "onServiceDisconnected, profile = " + profile);
        }
    };

    /**
     * 当前 Android 设备是否支持 Bluetooth
     *
     * @return true：支持 Bluetooth false：不支持 Bluetooth
     */
    public boolean isBluetoothSupported() {
        return bluetoothAdapter != null;
    }

    /**
     * 当前 Android 设备的 bluetooth 是否已经开启
     *
     * @return true：Bluetooth 已经开启 false：Bluetooth 未开启
     */
    public boolean isBluetoothEnabled() {
        if (bluetoothAdapter != null) {
            return bluetoothAdapter.isEnabled();
        }
        return false;
    }

    /**
     * 强制开启当前 Android 设备的 Bluetooth
     *
     * @return true：强制打开 Bluetooth　成功　false：强制打开 Bluetooth 失败
     */
    public boolean turnOnBluetooth() {
        if (bluetoothAdapter != null) {
            return bluetoothAdapter.enable();
        }
        return false;
    }

    /**
     * 关闭蓝牙
     *
     * @return
     */
    public boolean turnOffBluetooth() {
        if (bluetoothAdapter != null) {
            return bluetoothAdapter.disable();
        }
        return false;
    }

    /**
     * 开始扫描设备
     */
    public void startScan() {
        if (bluetoothAdapter.isDiscovering()) {
            return;
        }
        bluetoothAdapter.startDiscovery();
    }

    /**
     * 取消扫描
     */
    public void cancelScan() {
        if (bluetoothAdapter.isDiscovering()) {
            bluetoothAdapter.cancelDiscovery();
        }
    }

    /**
     * 返回已绑定的蓝牙设备列表
     *
     * @return List<SuperBlueToothDevice>
     */
    public List<SuperBluetoothDevice> getBondedDevices() {
        if (bluetoothAdapter != null) {
            Set<BluetoothDevice> bondedDevices = bluetoothAdapter.getBondedDevices();
            List<SuperBluetoothDevice> list = new ArrayList<>();
            try {
                Method isConnectedMethod = BluetoothDevice.class.getMethod("isConnected");
                for (BluetoothDevice bondedDevice : bondedDevices) {
                    boolean isConnected = (boolean) isConnectedMethod.invoke(bondedDevice);
                    list.add(new SuperBluetoothDevice(bondedDevice, bondedDevice.getName(),
                            true, isConnected));
                }
            } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                e.printStackTrace();
            }
            return list;
        }
        return null;
    }

    private HashMap<String, Map<String, BluetoothGattCharacteristic>> servicesMap = new HashMap<>();

    /**
     * 执行绑定 反射
     *
     * @param bluetoothDevice 蓝牙设备
     * @return true 执行绑定 false 未执行绑定
     */
    public boolean boundDevice(BluetoothDevice bluetoothDevice) {
        if (bluetoothDevice == null) {
            Log.e(TAG, "boundDevice-->bluetoothDevice == null");
            return false;
        }

        try {
            return ClsUtils.createBond(BluetoothDevice.class, bluetoothDevice);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return true;
    }

    /**
     * 执行解绑  反射
     *
     * @param bluetoothDevice 蓝牙设备
     * @return true 执行解绑  false未执行解绑
     */
    public boolean disBoundDevice(BluetoothDevice bluetoothDevice) {
        if (bluetoothDevice == null) {
            Log.e(TAG, "disBoundDevice-->bluetoothDevice == null");
            return false;
        }

        try {
            return ClsUtils.removeBond(BluetoothDevice.class, bluetoothDevice);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return true;
    }

    public void connectDevice(BluetoothDevice mTargetDevice) {
        switch (mTargetDevice.getBluetoothClass().getMajorDeviceClass()) {
            case BluetoothClass.Device.Major.PERIPHERAL:
                // HID
                try {
                    //通过反射获取BluetoothA2dp中connect方法
                    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P) {
                        Method connectMethod = BluetoothHidDevice.class.getMethod("connect",
                                BluetoothDevice.class);
                        Log.d(TAG, "connectMethod = " + connectMethod);
                        connectMethod.invoke(mBluetoothHidDevice, mTargetDevice);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
                break;
            default:
                int deviceClass = mTargetDevice.getBluetoothClass().getDeviceClass();
                switch (deviceClass) {
                    case BluetoothClass.Device.AUDIO_VIDEO_HIFI_AUDIO:
                    case BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES:
                    case BluetoothClass.Device.AUDIO_VIDEO_LOUDSPEAKER:
                    case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
                        try {
                            // 通过反射获取BluetoothA2dp中connect方法
                            Method connectMethod = BluetoothA2dp.class.getMethod("connect",
                                    BluetoothDevice.class);
                            Log.d(TAG, "connectMethod = " + connectMethod);
                            connectMethod.invoke(mBluetoothA2dpProfile, mTargetDevice);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        break;
                    default:
                        break;
                }
                break;
        }
    }

    @SuppressLint("HandlerLeak")
    private final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);

            switch (msg.what) {
                case START_DISCOVERY:
                    Log.d(TAG, "开始搜索设备...");
                    if (bluetoothScanResultCallback != null) {
                        bluetoothScanResultCallback.onScanStart();
                    }
                    break;

                case STOP_DISCOVERY:
                    Log.d(TAG, "停止搜索设备...");
                    if (bluetoothScanResultCallback != null) {
                        bluetoothScanResultCallback.onScanFinish();
                    }
                    break;

                case DISCOVERY_DEVICE:
                    // 扫描到设备
                    SuperBluetoothDevice bluetoothDevice = (SuperBluetoothDevice) msg.obj;
                    if (bluetoothScanResultCallback != null) {
                        bluetoothScanResultCallback.onScanDevice(bluetoothDevice);
                    }
                    break;

                case CONNECT_FAILURE:
                    // 连接失败
                    Log.d(TAG, "连接失败");
                    if (bluetoothConnectCallback != null) {
                        bluetoothConnectCallback.onConnectFailure();
                    }
                    break;

                case CONNECT_SUCCESS:
                    // 连接成功
                    Log.d(TAG, "连接成功");
                    if (bluetoothConnectCallback != null) {
                        bluetoothConnectCallback.onConnectSuccess();
                    }
                    break;

                case DISCONNECT_SUCCESS:
                    if (bluetoothConnectCallback != null) {
                        bluetoothConnectCallback.onDisconnectSuccess();
                    }
                    break;

                case SEND_FAILURE:
                    // 发送失败
                    if (dataSendReceiveCallback != null) {
                        dataSendReceiveCallback.onSendFailure();
                    }
                    break;

                case SEND_SUCCESS:
                    // 发送成功
                    String sendResult = (String) msg.obj;
                    if (dataSendReceiveCallback != null) {
                        dataSendReceiveCallback.onSendSuccess(sendResult);
                    }
                    break;

                case RECEIVE_FAILURE:
                    // 接收失败
                    String receiveError = (String) msg.obj;
                    if (dataSendReceiveCallback != null) {
                        dataSendReceiveCallback.onReceiveFailure(receiveError);
                    }
                    break;

                case RECEIVE_SUCCESS:
                    // 接收成功
                    String receiveResult = (String) msg.obj;
                    if (dataSendReceiveCallback != null) {
                        dataSendReceiveCallback.onReceiveSuccess(receiveResult);
                    }
                    break;

                case DEVICE_BOND_NONE:
                    // 已解除配对
                    if (bluetoothPairCallback != null) {
                        bluetoothPairCallback.onDeviceBondNone();
                    }
                    break;

                case DEVICE_BONDING:
                    // 正在配对
                    if (bluetoothPairCallback != null) {
                        bluetoothPairCallback.onDeviceBonding();
                    }
                    break;

                case DEVICE_BONDED:
                    // 已配对
                    if (bluetoothPairCallback != null) {
                        bluetoothPairCallback.onBonded();
                    }
                    break;

                default:
                    break;
            }
        }
    };

    /**
     * 开始连接设备
     *
     * @param bluetoothDevice 蓝牙设备
     * @param conOutTime      连接超时时间
     */
    public void startConnectDevice(final BluetoothDevice bluetoothDevice, long conOutTime) {
        if (bluetoothDevice == null) {
            Log.e(TAG, "startConnectDevice-->bluetoothDevice == null");
            return;
        }
        if (bluetoothAdapter == null) {
            Log.e(TAG, "startConnectDevice-->bluetooth3Adapter == null");
            return;
        }
        //发起连接
        connectThread = new ConnectThread(bluetoothAdapter, bluetoothDevice, MY_BLUETOOTH_UUID);
        connectThread.setOnBluetoothConnectListener(new ConnectThread.OnBluetoothConnectListener() {
            @Override
            public void onStartConn() {
                Log.d(TAG, "startConnectDevice-->开始连接..." + bluetoothDevice.getName() + "-->" + bluetoothDevice.getAddress());
            }

            @Override
            public void onConnSuccess(BluetoothSocket bluetoothSocket) {
                //移除连接超时
                mHandler.removeCallbacks(connectTimeoutRunnable);
                Log.d(TAG, "startConnectDevice-->移除连接超时");
                Log.w(TAG, "startConnectDevice-->连接成功");

                Message message = new Message();
                message.what = CONNECT_SUCCESS;
                mHandler.sendMessage(message);

                // 管理连接，收发数据
                managerConnectSendReceiveData(bluetoothSocket);
            }

            @Override
            public void onConnFailure(String errorMsg) {
                Log.e(TAG, "startConnectDevice-->" + errorMsg);

                Message message = new Message();
                message.what = CONNECT_FAILURE;
                mHandler.sendMessage(message);

                //断开管理连接
                clearConnectedThread();
            }
        });

        connectThread.start();
        //设置连接超时时间
        mHandler.postDelayed(connectTimeoutRunnable, conOutTime);
    }

    /**
     * 断开已有的连接
     */
    public void clearConnectedThread() {
        Log.d(TAG, "clearConnectedThread-->即将断开");

        // connectedThread断开已有连接
        if (connectedThread == null) {
            Log.e(TAG, "clearConnectedThread-->connectedThread == null");
            return;
        }
        connectedThread.terminalClose(connectThread);



        // 等待线程运行完后再断开
        mHandler.postDelayed(() -> {
            connectedThread.cancel();  //释放连接

            connectedThread = null;
        }, 10);

        Log.w(TAG, "clearConnectedThread-->成功断开连接");
        Message message = new Message();
        message.what = DISCONNECT_SUCCESS;
        mHandler.sendMessage(message);
    }

    /**
     * 管理已建立的连接，收发数据
     *
     * @param bluetoothSocket 已建立的连接
     */
    public void managerConnectSendReceiveData(BluetoothSocket bluetoothSocket) {
        // 管理已有连接
        connectedThread = new ConnectedThread(bluetoothSocket);
        connectedThread.start();
        connectedThread.setOnSendReceiveDataListener(new ConnectedThread.OnSendReceiveDataListener() {
            @Override
            public void onSendDataSuccess(byte[] data) {
                Log.w(TAG, "发送数据成功,长度" + data.length + "->" + bytes2HexString(data, data.length));
                Message message = new Message();
                message.what = SEND_SUCCESS;
                message.obj = "发送数据成功,长度" + data.length + "->" + bytes2HexString(data, data.length);
                mHandler.sendMessage(message);
            }

            @Override
            public void onSendDataError(byte[] data, String errorMsg) {
                Log.e(TAG, "发送数据出错,长度" + data.length + "->" + bytes2HexString(data, data.length));
                Message message = new Message();
                message.what = SEND_FAILURE;
                message.obj = "发送数据出错,长度" + data.length + "->" + bytes2HexString(data, data.length);
                mHandler.sendMessage(message);
            }

            @Override
            public void onReceiveDataSuccess(byte[] buffer) {
                Log.w(TAG, "成功接收数据,长度" + buffer.length + "->" + bytes2HexString(buffer, buffer.length));
                Message message = new Message();
                message.what = RECEIVE_SUCCESS;
                message.obj = "成功接收数据,长度" + buffer.length + "->" + bytes2HexString(buffer, buffer.length);
                mHandler.sendMessage(message);
            }

            @Override
            public void onReceiveDataError(String errorMsg) {
                Log.e(TAG, "接收数据出错：" + errorMsg);
                Message message = new Message();
                message.what = RECEIVE_FAILURE;
                message.obj = "接收数据出错：" + errorMsg;
                mHandler.sendMessage(message);
            }
        });
    }

    /**
     * 字节数组-->16进制字符串
     *
     * @param b      字节数组
     * @param length 字节数组长度
     * @return 16进制字符串 有空格类似“0A D5 CD 8F BD E5 F8”
     */
    public static String bytes2HexString(byte[] b, int length) {
        StringBuilder result = new StringBuilder();
        String hex;
        for (int i = 0; i < length; i++) {
            hex = Integer.toHexString(b[i] & 0xFF);
            if (hex.length() == 1) {
                hex = '0' + hex;
            }
            result.append(hex.toUpperCase()).append(" ");
        }
        return result.toString();
    }

    public void setScanResultCallback(BluetoothScanResultCallback callback) {
        bluetoothScanResultCallback = callback;
    }

    public void removeScanResultCallback() {
        bluetoothScanResultCallback = null;
    }

    public void setBluetoothPairCallback(BluetoothPairCallback callback) {
        bluetoothPairCallback = callback;
    }

    public void removeBluetoothPairCallback() {
        bluetoothPairCallback = null;
    }

    public void setBluetoothConnectCallback(BluetoothConnectCallback callback) {
        bluetoothConnectCallback = callback;
    }

    public void removeBluetoothConnectCallback() {
        bluetoothConnectCallback = null;
    }

    public void setDataSendReceiveCallback(BluetoothDataSendReceiveCallback callback) {
        dataSendReceiveCallback = callback;
    }

    public void removeDataSendReceiveCallback() {
        dataSendReceiveCallback = null;
    }

    /**
     * 蓝牙广播接收器
     */
    private class BtBroadcastReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (TextUtils.equals(action, BluetoothAdapter.ACTION_DISCOVERY_STARTED)) {
                // 开启搜索
                Log.d(TAG, "开启搜索...");
                Message message = new Message();
                message.what = START_DISCOVERY;
                mHandler.sendMessage(message);
            } else if (TextUtils.equals(action, BluetoothAdapter.ACTION_DISCOVERY_FINISHED)) {
                // 完成搜素
                Log.d(TAG, "停止搜索...");
                Message message = new Message();
                message.what = STOP_DISCOVERY;
                mHandler.sendMessage(message);
            } else if (TextUtils.equals(action, BluetoothDevice.ACTION_FOUND)) {
                // 3.0搜索到设备
                String name = intent.getStringExtra(BluetoothDevice.EXTRA_NAME);
                if (name != null) {
                    // 蓝牙设备
                    BluetoothDevice bluetoothDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                    // 信号强度
                    int rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, Short.MIN_VALUE);
                    Log.w(TAG, "扫描到设备：" + bluetoothDevice.getName() + "-->" + bluetoothDevice.getAddress()
                            + " type= " + bluetoothDevice.getType());
                    SuperBluetoothDevice myDevice = new SuperBluetoothDevice(bluetoothDevice, name, rssi);
                    Message message = new Message();
                    message.what = DISCOVERY_DEVICE;
                    message.obj = myDevice;
                    mHandler.sendMessage(message);
                }
            } else if (TextUtils.equals(action, BluetoothDevice.ACTION_BOND_STATE_CHANGED)) {
                BluetoothDevice bluetoothDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                int bondSate = bluetoothDevice.getBondState();
                switch (bondSate) {
                    case BluetoothDevice.BOND_NONE:
                        Log.d(TAG, "已解除配对");
                        Message message1 = new Message();
                        message1.what = DEVICE_BOND_NONE;
                        mHandler.sendMessage(message1);
                        break;

                    case BluetoothDevice.BOND_BONDING:
                        Log.d(TAG, "正在配对...");
                        Message message2 = new Message();
                        message2.what = DEVICE_BONDING;
                        mHandler.sendMessage(message2);
                        break;

                    case BluetoothDevice.BOND_BONDED:
                        Log.d(TAG, "已配对");
                        Message message3 = new Message();
                        message3.what = DEVICE_BONDED;
                        mHandler.sendMessage(message3);
                        break;

                    default:
                        break;
                }
            }
        }
    }

}